﻿//Copyright (c) 2009, Offbeat Guides
//All rights reserved.
//
//Redistribution and use in source and binary forms, with or without modification, are 
//permitted provided that the following conditions are met:
//
//* Redistributions of source code must retain the above copyright notice, this list of 
//conditions and the following disclaimer.
//
//* Redistributions in binary form must reproduce the above copyright notice, this list 
//of conditions and the following disclaimer in the documentation and/or other materials 
//provided with the distribution.
//
//* Neither the name of Offbeat Guides nor the names of its contributors may be used to 
//endorse or promote products derived from this software without specific prior written 
//permission.
//
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
//EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
//OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
//SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
//SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
//OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 
//TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
//EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Schema;
using System.Xml;
using System.Xml.Serialization;
using System.Text.RegularExpressions;
using System.IO;

namespace Amazon.Services.S3
{
    [Serializable]
    public class DirectoryInfo : Amazon.Services.S3.FileSystemInfo
    {
        #region Variables
        private System.DateTime _bufferTimestamp;
        private System.String[] _buffer;

        [NonSerialized]
        private Amazon.Services.S3.DriveInfo _drive;
        [NonSerialized]
        private Amazon.Services.S3.DirectoryInfo _parent;
        [NonSerialized]
        private Amazon.Services.S3.DirectoryInfo _root;
        #endregion

        #region Properties
        public DriveInfo Drive
        {
            get
            {
                if (this._drive == null)
                    this._drive = new DriveInfo(this.Client.Account, this.Bucket);
                return this._drive;
            }
        }

        public DirectoryInfo Parent
        {
            get
            {
                if (this._parent == null)
                {
                    Int32 index = this.FullName.LastIndexOf('\\');
                    this._parent = (index == -1) ? this.Root : new DirectoryInfo(this.Client.Account, this.FullName.Remove(index));
                }
                return this._parent;
            }
        }

        public DirectoryInfo Root
        {
            get
            {
                if (this._root == null)
                    this._root = String.IsNullOrEmpty(this.Name) ? this : new DirectoryInfo(this.Client.Account, String.Format("{0}:\\", this.Bucket));
                return this._root;
            }
        }

        public Boolean EnableCaching { get; set; }
        #endregion

        #region Overriden Properties
        protected override string Path
        {
            get { return base.Path.Replace(FileSystemHelper.MetadataFilename, String.Empty).TrimEnd('/'); }
        }

        public override string Name
        {
            get { return this.Path.LastIndexOf('/') >= 0 ? this.Path.Substring(this.Path.LastIndexOf('/') + 1) : this.Path; }
        }
        #endregion

        #region Constructors and Finalizers
        internal DirectoryInfo(ServiceClient client, String fullpath)
            : base(client, String.Format("{0}/{1}", fullpath.TrimEnd('/'), FileSystemHelper.MetadataFilename))
        {
            this._buffer = new String[0];
            this._bufferTimestamp = DateTime.MinValue;
        }

        internal DirectoryInfo(ServiceClient client, String bucket, String path)
            : base(client, bucket, FileSystemHelper.ToDirectory(path))
        {
            this._buffer = new String[0];
            this._bufferTimestamp = DateTime.MinValue;
        }

        public DirectoryInfo(AmazonAccount account, String fullpath)
            : base(new ServiceClient(account, S3Configuration.Default), String.Format("{0}\\{1}", fullpath.TrimEnd('\\'), FileSystemHelper.MetadataFilename))
        {
            this._buffer = new String[0];
            this._bufferTimestamp = DateTime.MinValue;
        }
        #endregion

        #region Methods
        public void Create()
        {
            if (this.Exists)
                throw new IOException("Directory already exists.");

            Match match = FileSystemHelper.PathRegex.Match(this.FullName);
            if (!match.Success)
                throw new ArgumentException(String.Format("Unable to locate the directory specified. directory = \"{0}\".", this.FullName));

            String bucket = match.Groups["bucket"].Value;
            if (bucket.Length > DriveInfo.MaxNameLength || bucket.Length < DriveInfo.MinNameLength)
                throw new ArgumentException("Drive length is beyond the accepted length.");

            String path = FileSystemHelper.NormalizePath(FileSystemHelper.ToDirectory(match.Groups["path"].Value));
            if (path.Length > DirectoryInfo.MaxPathLength || path.Length < DirectoryInfo.MinPathLength)
                throw new ArgumentException("Path length is beyond the accepted length.");

            FileSystemHelper.CreateFile(this.Client, bucket, path);
            this.Refresh();
        }

        public DirectoryInfo CreateSubdirectory(String name)
        {
            String path = String.Format("{0}\\{1}", this.FullName.TrimEnd('\\'), name);
            return Directory.CreateDirectory(this.Client.Account, path);
        }

        public void Delete(Boolean recursive)
        {
            Directory.Delete(this.Client.Account, this.FullName, recursive);
            this.Exists = false;
        }

        public DirectoryInfo[] GetDirectories()
        {


            if (DateTime.Now.Subtract(this._bufferTimestamp).TotalSeconds > 10 || !this.EnableCaching)
            {
                this._buffer = FileSystemHelper.GetObjectPaths(this.Client, this.Bucket, this.Path);
                this._bufferTimestamp = DateTime.Now;
            }

            Int32 level = this.Path.Split(new Char[] { '/' }, StringSplitOptions.RemoveEmptyEntries).Length + 1;
            List<DirectoryInfo> directories = new List<DirectoryInfo>();
            for (int i = 0; i < this._buffer.Length; i++)
            {
                if (this._buffer[i].Equals(base.Path, StringComparison.OrdinalIgnoreCase))
                    continue;

                Int32 pathLevel = this._buffer[i].Split(new Char[] { '/' }, StringSplitOptions.RemoveEmptyEntries).Length;
                if (pathLevel == (level + 1) && FileSystemHelper.IsDirectory(this._buffer[i]))
                    directories.Add(new DirectoryInfo(this.Client, this.Bucket, this._buffer[i]));
            }
            return directories.ToArray();
        }

        public FileInfo[] GetFiles()
        {

            if (DateTime.Now.Subtract(this._bufferTimestamp).TotalSeconds > 10 || !this.EnableCaching)
            {
                this._buffer = FileSystemHelper.GetObjectPaths(this.Client, this.Bucket, this.Path);
                this._bufferTimestamp = DateTime.Now;
            }

            Int32 level = this.Path.Split(new Char[] { '/' }, StringSplitOptions.RemoveEmptyEntries).Length + 1;
            List<FileInfo> files = new List<FileInfo>();
            for (int i = 0; i < this._buffer.Length; i++)
            {
                if (this._buffer[i].Equals(base.Path, StringComparison.OrdinalIgnoreCase))
                    continue;

                Int32 pathLevel = this._buffer[i].Split(new Char[] { '/' }, StringSplitOptions.RemoveEmptyEntries).Length;
                if (pathLevel == level && !FileSystemHelper.IsDirectory(this._buffer[i])) 
                    files.Add(new FileInfo(this.Client, this.Bucket, this._buffer[i]));
            }
            return files.ToArray();
        }

        public FileSystemInfo[] GetFileSystemInfos()
        {
            List<FileSystemInfo> fileSystems = new List<FileSystemInfo>();
            fileSystems.AddRange(this.GetDirectories());
            fileSystems.AddRange(this.GetFiles());
            return fileSystems.ToArray();
        }

        public void MoveTo()
        {
            throw new NotImplementedException();
        }
        #endregion

        #region Overriden Methods
        public override void Delete()
        {
            Directory.Delete(this.Client.Account, this.FullName, false);
            this.Exists = false;
        }
        #endregion
    }
}
